#include "config.h"

#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>

#define DBG_SUBSYS S_LIBTASK

#include "yid.h"
#include "schedule.h"
#include "token_bucket.h"
#include "nodectl.h"
#include "system.h"

#include "recovery.h"

extern hashtable_t recovery_tab;

typedef struct {
        // tocket bucket
        int is_init;
        int recovery_mode;
        int recovery_threshold_speed;
} recovery_qos_t;


recovery_qos_t g_recovery_qos = {
        .is_init = 0,
        .recovery_mode = 0,
        .recovery_threshold_speed = 0,
};

#define RECOVERY_THRESHOLD_SPEED    "recovery/threshold_speed"

static int get_recovery_threshold_speed(recovery_qos_t *qos)
{
        nodectl_file_t nf, *pnf = &nf;
        opt_data_init(pnf, RECOVERY_THRESHOLD_SPEED);

        qos->recovery_threshold_speed = pnf->get_int(pnf, 0);
        return 0;
}

static int recovery_threshold_speed(void *context, uint32_t mask)
{
        (void) context;
        (void) mask;

        get_recovery_threshold_speed(&g_recovery_qos);

        return g_recovery_qos.recovery_threshold_speed;
}

static int recovery_threshold_speed_reset(void *context, uint32_t mask)
{
        int ret;

        (void) context;
        (void) mask;
        ret = opt_config_register(RECOVERY_THRESHOLD_SPEED, "0", recovery_threshold_speed,
                                recovery_threshold_speed_reset, NULL);
        if (unlikely(ret))
                UNIMPLEMENTED(__DUMP__);

        return 0;
}

static int __recovery_qos_init(recovery_qos_t *qos)
{
        int ret;

        qos->is_init = 1;
        ret = opt_config_register(RECOVERY_THRESHOLD_SPEED, "0", recovery_threshold_speed,
                                recovery_threshold_speed_reset, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;

}

static int __recovery_qos_rule(recovery_qos_t *qos, const char *name)
{
        recovery_pool_t *pool;

        if(unlikely(recovery_tab == NULL)){
            return 0;
        }

        pool = hash_table_find(recovery_tab, (void *)name);
        if (likely(pool == NULL || pool->status != __RECOVERY_RUNNING__)) {
                return 0;
        }

        if (unlikely(!qos->is_init)) {
                __recovery_qos_init(qos);
        }

        if (likely(qos->recovery_mode == PREFER_APP)) {
                // nothing to do
        } else if (qos->recovery_mode == PREFER_RECOVERY) {
                /**
                 * 如果pool处在数据恢复中，并且策略是恢复优先
                 * 通过调节IO流量，来达到保证恢复流量的目的
                 *
                 * 恢复流量是控制目标
                 */
                int delay = 10;
                int loop = 0;

                // YASSERT(qos->speed > 0);
                // speed => qos rule
                while (qos->recovery_threshold_speed) {
                        schedule_sleep("recovery_qos", delay*loop);

                        loop ++;
                        if (loop > qos->recovery_threshold_speed) {
                                break;
                        }
                }
        }

        return 0;
}

static inline int __recovery_qos_set_mode(recovery_qos_t *qos, int mode) {
        if (mode == PREFER_APP || mode == PREFER_RECOVERY) {
                qos->recovery_mode = mode;
        } else {
                DWARN("invalid %d\n", mode);
        }

        return 0;
}

// export APIs

int recovery_qos_set_mode(int mode) {
        return __recovery_qos_set_mode(&g_recovery_qos, mode);
}

int recovery_qos_apply(const char *name) {
        return __recovery_qos_rule(&g_recovery_qos, name);
}
